package job

import (
	"fmt"
	"log/slog"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"gorm.io/gorm"

	"torler.com/qingyue/apps/importer/config"
	"torler.com/qingyue/common/model"
	"torler.com/qingyue/common/types"
	"torler.com/qingyue/pkg/env"
	jfile "torler.com/qingyue/pkg/file"
	"torler.com/qingyue/pkg/jstring"
)

const (
	IMPORT_BATCH_SIZE = 10 // 每批处理的文件数量
	IMPORT_VERSION    = 3  // 导入程序版本

)

var (
	SUPPORT_SUBFIX = []string{".txt"}                                                    // 支持的文件后缀
	ROOT_DIRS      = []string{"/mnt/novel/download", "/mnt/ebook", "/mnt/novel/renamed"} // 扫描的根目录
	ROOT_DIRS_TEST = []string{"/mnt/novel/test"}
	//整理后的存放目录
	TARGET_IMPORTED_DIR = "/mnt/novel/imported"
	//未整理后的存放目录
	TARGET_ORIGIN_DIR = "/mnt/novel/origin"
	//整理且分割章节后的存放目录
	TARGET_PRODUCT_DIR = "/mnt/novel/product"
)

type BookImporter struct {
	root []string
	db   *gorm.DB
	log  *slog.Logger
	rule *config.Rule
}

func init() {
	os.MkdirAll(TARGET_IMPORTED_DIR, os.ModePerm)
	os.MkdirAll(TARGET_PRODUCT_DIR, os.ModePerm)
	os.MkdirAll(TARGET_ORIGIN_DIR, os.ModePerm)
}

func (b *BookImporter) Name() string { return "小说导入" }
func (b *BookImporter) Corn() string { return "0 * * * * *" }

func NewBookImporter(db *gorm.DB, rule *config.Rule, log *slog.Logger) *BookImporter {

	if env.IsDev() {
		return &BookImporter{
			root: ROOT_DIRS_TEST,
			db:   db,
			log:  log,
			rule: rule,
		}
	} else {
		return &BookImporter{
			root: ROOT_DIRS,
			db:   db,
			log:  log,
			rule: rule,
		}
	}

}

func (b *BookImporter) Run() error {

	logger := b.log
	logger.Info("开始导入小说")
	for _, parent := range b.root {
		b.scanAllBookFiles(parent, b.processBookFile)

	}

	return nil
}

// processBookFile 处理单个小说文件的导入
func (it *BookImporter) processBookFile(path string) {

	db := it.db
	logger := it.log.With("file_path", path)
	logger.Info("开始处理文件")
	fileMd5, err := jfile.GetMd5(path)

	if err != nil {
		logger.Error("获取文件MD5失败", "error", err)
		return
	}

	logger = logger.With("file_md5", fileMd5)

	var book model.RawBook

	db.Where("oid = ?", fileMd5).First(&book)
	extra := book.GetExtraAsImportedExtra()
	if extra == nil {
		extra = model.NewImportedExtra(IMPORT_VERSION, path)
		book.SetExtra(extra)
	}

	defer func() {
		//忽略内容过少的小说
		if book.MetaState == types.MetaStateNotBook {
			os.Remove(path)
			logger.Info("忽略的文件，跳过导入")
			return
		}
		if book.MetaState == types.MetaStateImportedError {
			targetPath := filepath.Join(TARGET_ORIGIN_DIR, extra.FileName)
			err := os.Rename(extra.FilePath, targetPath)
			if err != nil {
				logger.Error("移动文件失败", "error", err)
			} else {
				logger.Info("移动文件成功")
				extra.FilePath = targetPath
			}

		}
		logger.Info("保存小说信息", "book_name", book.BookName, "author", book.Author, "meta_state", book.MetaState, "path", extra.FilePath)
		book.SetExtra(extra)
		db.Save(&book)
	}()
	// 文件已存在且版本无需更新
	if book.OId != "" && extra.ImportVersion >= IMPORT_VERSION && book.MetaState == types.MetaStateImported {
		logger.Info("文件已存在且版本无需更新")
		os.Remove(path)
		return
	}
	if book.OId == "" {
		book.OId = types.NewOriginId(fileMd5)
		book.SourceId = types.SourceIdLocal
		extra.FileName = filepath.Base(path)
		extra.FilePath = path
		extra.ImportVersion = IMPORT_VERSION
	}
	//从文件名中提取书名和作者
	bookName1, author1 := extractBookInfoFromFileName(extra.FileName)

	lines, err := jfile.ReadFile(path)
	if err != nil || lines == nil || len(*lines) == 0 {
		logger.Error("读取文件失败", "error", err)
		return
	}
	//少于一千行，判定非小说则忽略
	if len(*lines) < 10000 {
		book.MetaState = types.MetaStateNotBook
		logger.Info("文件内容过少，跳过导入")
		return
	}
	//从文件名中获取到书名或作者，则尝试从文件内容前50行中获取
	topLines := (*lines)[:50]
	name2, author2 := extractBookInfoFromContext(topLines)

	if author1 == "" || bookName1 == "" {
		book.BookName = name2
		book.Author = author2

	} else {
		book.BookName = bookName1
		book.Author = author1
	}
	//若从文件内容中获取到作者，可能包含/符号，此时使用name1中
	if strings.Contains(name2, "/") {
		if bookName1 != "" {
			book.BookName = bookName1
		} else {
			//使用name2前/部分作为书名
			parts := strings.SplitN(name2, "/", 2)
			book.BookName = strings.TrimSpace(parts[0])
		}
	}

	book.BookName = strings.Trim(book.BookName, "『』━-《》〈〉[]【】 ()")
	book.Author = strings.Trim(book.Author, "『』━-《》〈〉[]【】 ()")

	logger.Info("处理书名和作者信息完成：", "book_name", book.BookName, "author", book.Author)
	if book.BookName == "" || book.Author == "" {
		book.MetaState = types.MetaStateImportedError
		return
	}

	//移动到整理目录
	targetName := fmt.Sprintf("%s-%s.txt", book.BookName, book.Author)
	targetPath := filepath.Join(TARGET_IMPORTED_DIR, targetName)

	//将lines中的内容写入到targetPath
	fileSize, err := jfile.WriteLinesToFile(lines, targetPath)
	if err != nil {
		logger.Error("写入重命名文件失败", "error", err)
		book.MetaState = types.MetaStateImportedError
		return
	}
	extra.FileSize = int(fileSize)
	err = os.Remove(path)
	if err != nil {
		logger.Error("删除原文件失败", "error", err)
		book.MetaState = types.MetaStateImportedError
		return
	}
	book.MetaState = types.MetaStateImported
	extra.FilePath = targetPath
	extra.FileName = targetName

}

// scanAllBookFiles 扫描目录，返回所有.txt文件路径
func (b *BookImporter) scanAllBookFiles(parent string, process func(paht string)) error {

	err := filepath.WalkDir(parent, func(path string, d os.DirEntry, err error) error {
		if err != nil {

			return err
		}
		if !d.IsDir() {
			fileName := jfile.GetFileNameNoExt(path)
			// 忽略隐藏文件
			if strings.HasPrefix(fileName, ".") {
				return nil

			}
			subfix := filepath.Ext(path)

			// 若在支持的后缀列表中，则添加到结果中
			if jstring.SliceContainIgnoreCase(SUPPORT_SUBFIX, subfix) >= 0 {
				process(path)
			}

		}
		return nil
	})

	return err
}

func extractBookInfoFromFileName(fileName string) (string, string) {
	fileName = jfile.GetFileNameNoExt(fileName)
	// 提取书名的正则 - 匹配《》之间的内容，如果没有则匹配开头到作者部分的内容
	bookNameRegex := regexp.MustCompile(`《([^《》]+)》`)
	// 提取作者的正则 - 匹配BY或作者：/作者后面的内容
	authorRegex := regexp.MustCompile(`(?i)(?:BY|作者[:：]?)\s*([^\s\(（\[\{《〈]+)`)

	var bookName, author string

	// 提取书名
	bookNameMatches := bookNameRegex.FindStringSubmatch(fileName)
	if len(bookNameMatches) > 1 {
		bookName = strings.TrimSpace(bookNameMatches[1])
	} else {
		// 如果没有找到书名号，则尝试从开头提取书名直到作者部分
		authorPartIndex := regexp.MustCompile(`(?i)(?:BY|作者[:：]?)`).FindStringIndex(fileName)
		if len(authorPartIndex) > 0 {
			bookName = strings.TrimSpace(fileName[:authorPartIndex[0]])
		}
	}

	// 提取作者
	authorMatches := authorRegex.FindStringSubmatch(fileName)
	if len(authorMatches) > 1 {
		author = strings.TrimSpace(authorMatches[1])
	}
	if (bookName == "" || author == "") && strings.Contains(fileName, "-") {
		parts := strings.SplitN(fileName, "-", 2)
		bookName = strings.TrimSpace(parts[0])
		author = strings.TrimSpace(parts[1])

	}

	// 在返回前处理，使用正则去除括号中的内容
	// 去除书名中"+"及之后的内容
	if plusIndex := strings.Index(bookName, "+"); plusIndex != -1 {
		bookName = strings.TrimSpace(bookName[:plusIndex])
	}

	// 使用正则表达式去除各种括号及其内部内容
	// 匹配圆括号、方括号、尖括号等及其中的内容
	bracketRegex := regexp.MustCompile(`[（(［\[{｛〈《【].*?[】》〉｝\}\]］)）]`)
	bookName = bracketRegex.ReplaceAllString(bookName, "")
	author = bracketRegex.ReplaceAllString(author, "")

	// 去除可能残留的空格
	bookName = strings.TrimSpace(bookName)
	author = strings.TrimSpace(author)

	return bookName, author
}

// 从文件内容中提取书名和作者
// lines 小说文件内容的前50行
func extractBookInfoFromContext(lines []string) (bookName string, author string) {
	bookName = ""
	author = ""

	for index, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		// 1. 单行
		// 《书名》作者：xxx
		r1, err := regexp.Compile(`(?i)[《〈](.+?)[》〉]\s*(?:作者[:：]?|BY)\s*([A-Za-z0-9\x{4e00}-\x{9fa5}_·]+)`)
		if err != nil {
			continue
		}
		if m := r1.FindStringSubmatch(line); len(m) == 3 {
			bookName = m[1]
			author = m[2]
			break
		}
		// 2.单行
		//  书名 作者：xxx
		r2, err := regexp.Compile(`(.+?)\s*作者[:：]\s*([A-Za-z0-9\x{4e00}-\x{9fa5}_·]+)`)
		if err != nil {
			continue
		}
		if m := r2.FindStringSubmatch(line); len(m) == 3 {
			bookName = m[1]
			author = m[2]
			break

		}
		// 3.有换行
		// 《xxxxxx》
		// 作者：xxxxx
		if strings.HasPrefix(line, "作者：") {
			author = strings.TrimSpace(strings.Split(line, "作者：")[1])
			//防止越界
			if index-1 >= 0 {
				bookName = lines[index-1]
				//若有书名号则提取书名号中的内容为书名
				re3, err := regexp.Compile(`[《〈](.+?)[》〉]`)
				if err != nil {
					continue
				}
				if m := re3.FindStringSubmatch(bookName); len(m) == 2 {
					bookName = m[1]
				}
			}

			break
		}

		// 4.单行 著
		//[神仙也有江湖 / 柳暗花溟 著 ]
		re4, err := regexp.Compile(`\[([^\/\]]+)\s*/\s*([^\]著]+)\s*著\s*\]`)
		if err != nil {
			continue
		}
		if m := re4.FindStringSubmatch(line); len(m) == 3 {
			bookName = m[1]
			author = m[2]
			break
		}

	}
	bookName = strings.TrimPrefix(bookName, "书名：")

	author = strings.TrimPrefix(author, "作者：")

	// 使用正则表达式去除各种括号及其内部内容
	// 匹配圆括号、方括号、尖括号等及其中的内容
	bracketRegex := regexp.MustCompile(`[<（(［\[{｛〈《【].*?[】》〉｝\}\]］)）>]`)
	bookName = bracketRegex.ReplaceAllString(bookName, "")
	author = bracketRegex.ReplaceAllString(author, "")
	bookName = strings.TrimSuffix(bookName, "/")
	bookName = strings.TrimSuffix(bookName, "全文免费阅读")

	author = strings.TrimSpace(author)
	bookName = strings.TrimSpace(bookName)
	return bookName, author

}
